home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Developer Essentials / DTS Sample Code / System 7.0 Samples / Kibitz / AEchess.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-02-21  |  44.0 KB  |  1,509 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:        Kibitz
  5. ** File:        AEchess.c
  6. ** Written by:  Eric Soldan
  7. **
  8. ** Copyright © 1990-1991 Apple Computer, Inc.
  9. ** All rights reserved.
  10. **
  11. ** This is the custom AppleEvents code.  The required AppleEvents code is in
  12. ** the file AppleEvents.c, which is Keith Rollin's work.
  13. */
  14.  
  15.  
  16.  
  17. /*****************************************************************************/
  18.  
  19.  
  20.  
  21. #include "Kibitz.h"                /* Get the Kibitz includes/typedefs, etc.    */
  22. #include "KibitzCommon.h"        /* Get the stuff in common with rez.        */
  23. #include "Kibitz.protos"        /* Get the prototypes for Kibitz.            */
  24.  
  25. #ifndef __ERRORS__
  26. #include <Errors.h>
  27. #endif
  28.  
  29. #ifndef __MEMORY__
  30. #include <Memory.h>
  31. #endif
  32.  
  33. #ifndef __NOTIFICATION__
  34. #include <Notification.h>
  35. #endif
  36.  
  37. #ifndef __OSUTILS__
  38. #include <OSUtils.h>
  39. #endif
  40.  
  41. #ifndef __RESOURCES__
  42. #include <Resources.h>
  43. #endif
  44.  
  45. #ifndef __SOUND__
  46. #include <Sound.h>
  47. #endif
  48.  
  49. #ifndef __STRING__
  50. #include <String.h>
  51. #endif
  52.  
  53. #ifndef __SYSEQU__
  54. #include <SysEqu.h>
  55. #endif
  56.  
  57. #ifndef __TOOLUTILS__
  58. #include <ToolUtils.h>
  59. #endif
  60.  
  61. #ifndef __TEXTEDITCONTROL__
  62. #include <TextEditControl.h>
  63. #endif
  64.  
  65. #ifndef __UTILITIES__
  66. #include <Utilities.h>
  67. #endif
  68.  
  69.  
  70.  
  71. /*****************************************************************************/
  72.  
  73.  
  74.  
  75. #define PRIORITY        kAENormalPriority
  76. #define kTimeOutInTicks (60 * 30)    /* 30 second timeout. */
  77.  
  78.  
  79.  
  80. /*****************************************************************************/
  81.  
  82.  
  83.  
  84. static Boolean        notifyActive = false;
  85. static pascal void    CancelNotify(NMRecPtr nmReqPtr);
  86. static NMRec        notifyTheUser = {
  87.     nil,        /* qLink */
  88.     nmType,        /* qType */
  89.     0,            /* nmFlags */
  90.     0L,            /* nmPrivate */
  91.     0,            /* nmReserved */
  92.     1,            /* nmMark */
  93.     nil,        /* nmIcon */
  94.     -1,            /* nmSound */
  95.     nil,        /* nmStr */
  96.     nil,        /* nmResp */
  97.     0L            /* nmRefCon */
  98. };
  99.  
  100.  
  101.  
  102. /*****************************************************************************/
  103.  
  104.  
  105.  
  106. struct triplets{
  107.     AEEventClass    theEventClass;
  108.     AEEventID        theEventID;
  109.     ProcPtr            theHandler;
  110. };
  111. typedef struct triplets triplets;
  112. static triplets keywordsToInstall[] = {
  113.     { kCoreEventClass,        kAEAnswer,                DoAEAnswer },
  114.     { kCustomEventClass,    kibitzAESendGame,        ReceiveGame },
  115.     { kCustomEventClass,    kibitzAESendMssg,        ReceiveMssg }
  116. };        /* These are the custom AppleEvents. */
  117.  
  118.  
  119.  
  120. /*****************************************************************************/
  121.  
  122.  
  123.  
  124. extern Boolean        gHasAppleEvents;
  125. extern Boolean        gHasPPCToolbox;
  126.  
  127. extern RgnHandle    gCurrentCursorRgn;
  128. extern Cursor        *gCurrentCursor;
  129.  
  130.  
  131.  
  132. /*****************************************************************************/
  133. /*****************************************************************************/
  134.  
  135.  
  136.  
  137. /* InitCustomAppleEvents
  138. **
  139. ** Install our custom AppleEvents.  This is done in addition to installing
  140. ** the required AppleEvents.  InitAppleEvents, which installs the required
  141. ** AppleEvents, must be called first, since it sets up some global values.
  142. */
  143.  
  144. #pragma segment AppleEvents
  145. void    InitCustomAppleEvents(void)
  146. {
  147.     OSErr    err;
  148.     short    i;
  149.  
  150.     if (gHasAppleEvents) {
  151.         for (i = 0; i < (sizeof(keywordsToInstall) / sizeof(triplets)); ++i) {
  152.             err = AEInstallEventHandler(
  153.                 keywordsToInstall[i].theEventClass,    /* What class to install.  */
  154.                 keywordsToInstall[i].theEventID,    /* Keywords to install.    */
  155.                 keywordsToInstall[i].theHandler,    /* The AppleEvent handler. */
  156.                 0L,                                    /* Unused refcon.           */
  157.                 false                                /* Only for our app.       */
  158.             );
  159.  
  160.             if (err) {
  161.                 Alert(rErrorAlert, nil);
  162.                 return;
  163.             }
  164.         }
  165.     }
  166. }
  167.  
  168.  
  169.  
  170. /*****************************************************************************/
  171.  
  172.  
  173.  
  174. #pragma segment AppleEvents
  175. pascal OSErr    DoAEAnswer(AppleEvent message, AppleEvent reply, long refcon)
  176. {
  177. #pragma unused (refcon)
  178.  
  179.     OSErr    err;
  180.  
  181.     gCurrentCursor = nil;
  182.         /* Force re-calc of cursor region and cursor to use. */
  183.  
  184.     err = ReceiveGameReply(message, reply);
  185.  
  186.     AEPutParamPtr(                /* RETURN REPLY ERROR, EVEN IF NONE... */
  187.         &reply,                    /* The AppleEvent.              */
  188.         keyReplyErr,            /* AEKeyword                 */
  189.         typeShortInteger,        /* Desired type.             */
  190.         (Ptr)&err,                /* Pointer to area for data. */ 
  191.         sizeof(short)            /* Size of data area.         */
  192.     );
  193.  
  194.     return(noErr);
  195. }
  196.  
  197.  
  198.  
  199. /*****************************************************************************/
  200.  
  201.  
  202.  
  203. /* SendGame
  204. **
  205. ** This routine acquires an opponent and sends the opponent the current game.
  206. ** Various changes are made to the game record to indicate that this game
  207. ** has an opponent, which color we are, where we are located (so the
  208. ** opponent can send moves back), the ID of our game, etc.  Once this
  209. ** information is in the game record, the game is transmitted to the
  210. ** opponent.  Upon receiving the game, the opponent sends back confirmation,
  211. ** which also includes the ID of the game on the opponent's end.  The return
  212. ** information is sent back as via AEAnswer, so we will extablish a two-player
  213. ** game until we receive this event.  Once we do establish a two-player game,
  214. ** we will store the opponent's game ID in the game record as well.  With our
  215. ** ID and the opponents ID, we will be able to determine which game the data
  216. ** is for at both ends.  This allows two opponents to play multiple games with
  217. ** the same machines.
  218. */
  219.  
  220. #pragma segment AppleEvents
  221. OSErr    SendGame(FileRecHndl frHndl, short sendReason)
  222. {
  223.     AEAddressDesc    locOfOpponent;
  224.     Boolean            twoPlayer;
  225.     OSErr            err;
  226.     AEDescList        sendGameList;
  227.     long            gameID[2], size;
  228.     char            hstate;
  229.     Ptr                ptr1, ptr2;
  230.     GameListHndl    gameMoves;
  231.     AppleEvent        theAevt, reply;
  232.     short            replyType;
  233.     Str255            macText, appText, userName;
  234.     Handle            userNameHndl;
  235.  
  236.     sendGameList.dataHandle = theAevt.dataHandle = reply.dataHandle = nil;
  237.         /* Make sure disposing of the descriptors is okay in all cases.
  238.         ** This will not be necessary after 7.0b3, since the calls that
  239.         ** attempt to create the descriptors will nil automatically
  240.         ** upon failure. */
  241.  
  242.     err = noErr;
  243.         /* We may not make the first operation that can cause an error,
  244.         ** so we have to initialize err. */
  245.  
  246.     locOfOpponent.dataHandle = nil;
  247.         /* Make it safe to dispose of in all cases. */
  248.  
  249.     if ((*frHndl)->doc.resync < ((*frHndl)->doc.sendReason = sendReason))
  250.         (*frHndl)->doc.resync = sendReason;
  251.             /* Bump up resync, if needed. */
  252.  
  253.     UpdateTime(frHndl, false);
  254.         /* Make sure timers are current before we give them to opponent. */
  255.  
  256.     twoPlayer = (*frHndl)->doc.twoPlayer;
  257.     if (!twoPlayer) {            /* If we don't have a live opponent yet... */
  258.         GetIndString(macText, rPPCText, sTitleText);
  259.         GetIndString(appText, rPPCText, sAppText);
  260.         err = MakeTarget(&locOfOpponent, false, kAEWaitReply,
  261.             macText, appText, KibitzPortFilter);
  262.                 /* Generate the target for our opponent. */
  263.         (*frHndl)->doc.gameID_0      = gameID[0] = TickCount();
  264.         (*frHndl)->doc.gameID_1      = gameID[0];
  265.         (*frHndl)->doc.locOfOpponent = locOfOpponent;
  266.     }
  267.     else locOfOpponent = (*frHndl)->doc.locOfOpponent;
  268.  
  269.     if (!err) {        /* If we have an opponent... */
  270.         err = AECreateList(        /* CREATE THE LIST TO HOLD THE GAME. */
  271.             nil,                /* No factoring.             */
  272.             0,                    /* No factoring.             */
  273.             false,                /* Not an AppleEvent record. */
  274.             &sendGameList        /* List descriptor.             */
  275.         );
  276.     }
  277.  
  278.     if (!err) {        /* If we have an empty list to add to... */
  279.         hstate = LockHandleHigh((Handle)frHndl);
  280.         ptr1   = (Ptr)&((*frHndl)->doc);
  281.         ptr2   = (Ptr)&((*frHndl)->doc.endSendInfo);
  282.         size   = (long)ptr2 - (long)ptr1;
  283.         err = AEPutPtr(        /* ADD BOARD INFO TO THE APPLEEVENT. */
  284.             &sendGameList,    /* List to add to.                     */
  285.             1L,                /* Make this element #1.             */
  286.             typeTheBoard,    /* It is a descriptor for the board. */
  287.             ptr1,            /* Pointer to the board data.         */
  288.             size            /* Size of the board data.             */
  289.         );
  290.         HSetState((Handle)frHndl, hstate);
  291.     }
  292.  
  293.     /* For a new opponent, the field twoPlayer in sendGameList (to be sent
  294.     ** to the opponent) indicates that there is no live opponent.  When the
  295.     ** receiver gets a board with twoPlayer false, this indicates a new game
  296.     ** is being set up.  If twoPlayer is true, then it is an existing game. */
  297.  
  298.     if (!err) {        /* If we could add the board info to the list... */
  299.         gameMoves = (*frHndl)->doc.gameMoves;
  300.         size      = (*frHndl)->doc.numGameMoves * sizeof(GameElement);
  301.         hstate    = LockHandleHigh((Handle)gameMoves);
  302.         err = AEPutPtr(            /* ADD GAME MOVES TO THE LIST.             */
  303.             &sendGameList,        /* List to add to.                         */
  304.             2L,                    /* Make this element #2.                 */
  305.             typeGameMoves,        /* Descriptor for the moves of the game. */
  306.             (Ptr)(*gameMoves),    /* Pointer to the move data.             */
  307.             size                /* Size of the move data.                 */
  308.         );
  309.         HSetState((Handle)gameMoves, hstate);
  310.     }
  311.  
  312.     if ((!err) && (!twoPlayer)) {
  313.         hstate = LockHandleHigh((Handle)frHndl);
  314.         ptr1   = (Ptr)&((*frHndl)->fileState.fss);
  315.         err = AEPutPtr(            /* ADD FSSpec TO LIST TO PASS THE GAME NAME. */
  316.             &sendGameList,        /* List to add to.         */
  317.             3L,                    /* Make this element #3. */
  318.             typeFSS,            /* FSSpec descriptor.     */
  319.             ptr1,                /* Pointer to the data.     */
  320.             sizeof(FSSpec)        /* Size of the data.     */
  321.         );
  322.         HSetState((Handle)frHndl, hstate);
  323.         if (!err) {
  324.             userName[0] = 0;
  325.             if (userNameHndl = GetResource('STR ', -16096))
  326.                 pstrcpy(userName, (char *)(*userNameHndl));
  327.             err = AEPutPtr(            /* ADD USER NAME TO LIST. */
  328.                 &sendGameList,        /* List to add to.             */
  329.                 4L,                    /* Make this element #4.     */
  330.                 typePascal,            /* Pascal string descriptor. */
  331.                 userName,            /* Pointer to the data.         */
  332.                 userName[0] + 1        /* Size of the data.         */
  333.             );
  334.         }
  335.     }
  336.  
  337.     if (!err) {        /* If we could add the game moves to the list... */
  338.         err = AECreateAppleEvent(            /* CREATE EMPTY APPLEEVENT.     */
  339.             kCustomEventClass,                /* Event class.                 */
  340.             kibitzAESendGame,                /* Event ID.                 */
  341.             &locOfOpponent,                    /* Address of receiving app. */
  342.             kAutoGenerateReturnID,            /* This value causes the     */
  343.                                             /* AppleEvent manager to     */
  344.                                             /* assign a return ID that     */
  345.                                             /* is unique to the session. */
  346.             kAnyTransactionID,                /* Ignore transaction ID.     */
  347.             &theAevt                        /* Location of event.         */
  348.         );
  349.     }
  350.  
  351.     if (!err) {        /* If we have an empty AppleEvent... */
  352.         err = AEPutParamDesc(    /* PUT THE LIST INTO THE APPLEEVENT.  */
  353.             &theAevt,            /* AppleEvent to add list to.          */
  354.             keyDirectObject,    /* This is our direct (only) object.  */
  355.             &sendGameList        /* The list to add to the AppleEvent. */
  356.         );
  357.     }
  358.  
  359.     replyType = (!twoPlayer) ? kAEQueueReply : kAENoReply;
  360.         /* Queue a reply only for a new game. */
  361.  
  362.     if (!err) {        /* If we have an AppleEvent ready to send... */
  363.         err = AESend(        /* SEND APPLEEVENT.                */
  364.             &theAevt,        /* Our Apple Event to send.        */
  365.             &reply,            /* We may have a reply.            */
  366.             replyType,        /* Type of reply.                */
  367.             PRIORITY,        /* App. send priority.            */
  368.             0,                /* We aren't waiting.            */
  369.             nil,            /* We aren't waiting.            */
  370.             nil                /* EventFilterProcPtr.            */
  371.         );
  372.     }
  373.     if (replyType == kAEQueueReply)
  374.         if (locOfOpponent.descriptorType == typeProcessSerialNumber)
  375.             err = ReceiveGameReply(reply, reply);
  376.                 /* If we want q queue reply, and if we are sending to ourselves,
  377.                 ** then we already have the reply.  Since we are sending to
  378.                 ** ourselves, we don't have to wait for an event.  Stuff happens
  379.                 ** right away.  We're (probably) happy. */
  380.  
  381.     AEDisposeDesc(&sendGameList);
  382.     AEDisposeDesc(&theAevt);
  383.     AEDisposeDesc(&reply);
  384.         /* Dispose of the descriptors, created or not.
  385.         ** If not created, no harm done by calling. */
  386.  
  387.     if (err) {
  388.         AEDisposeDesc(&locOfOpponent);
  389.             /* If we didn't connect, get rid of the target descriptor. */
  390.  
  391.         (*frHndl)->doc.gameID_0 = 0;
  392.         (*frHndl)->doc.gameID_1 = 0;
  393.             /* Mark this window so that it will never be found if we somehow
  394.             ** do get an answer from the receiver, even after failure. */
  395.     }
  396.  
  397.     return(err);
  398. }
  399.  
  400.  
  401.  
  402. /*****************************************************************************/
  403.  
  404.  
  405.  
  406. /* This code is executed only after an game is initially sent and we receive
  407. ** an AEAnswer event.  The "answer" is acknowledgment that the opponent
  408. ** received the game and that the opponent is set up for a two-player game.
  409. ** In the reply, we receive the opponent's game ID, which is used to match
  410. ** up which game receives the AppleEvents.
  411. */
  412.  
  413. #pragma segment AppleEvents
  414. pascal OSErr    ReceiveGameReply(AppleEvent message, AppleEvent reply)
  415. {
  416. #pragma unused (reply)
  417.  
  418.     OSErr            err, replyErr;
  419.     DescType        actualType;
  420.     long            gameID[2], actualSize;
  421.     WindowPtr        window;
  422.     FileRecHndl        frHndl;
  423.     Str255            userName;
  424.  
  425.     err = AEGetParamPtr(        /* CHECK FOR A RECEIVER ERROR... */
  426.         &message,                /* The AppleEvent.              */
  427.         keyReplyErr,            /* AEKeyword                 */
  428.         typeShortInteger,        /* Desired type.             */
  429.         &actualType,            /* Type code.                 */
  430.         (Ptr)&replyErr,            /* Pointer to area for data. */ 
  431.         sizeof(short),            /* Size of data area.         */
  432.         &actualSize                /* Returned size of data.     */
  433.     );
  434.     if (!err) err = replyErr;
  435.  
  436.     if (!err) {
  437.         err = AEGetParamPtr(    /* GET RECEIVER GAME ID. */
  438.             &message,            /* The AppleEvent.              */
  439.             keyGameID,            /* AEKeyword                 */
  440.             typeDoubleLong,        /* Desired type.             */
  441.             &actualType,        /* Type code.                 */
  442.             (Ptr)&gameID[0],    /* Pointer to area for data. */ 
  443.             2 * sizeof(long),    /* Size of data area.         */
  444.             &actualSize            /* Returned size of data.     */
  445.         );
  446.     }
  447.  
  448.     if (!err) {        /* If we got the receiver game ID... */
  449.  
  450.         window = GetGameWindow(gameID[1], gameID[1]);
  451.             /* The ID's are still both ours, since this is where we
  452.             ** get the receiver's ID returned.  gameID[0] holds the
  453.             ** receiver's ID, and gameID[1] holds ours. */
  454.  
  455.         if (window) {
  456.             frHndl = (FileRecHndl)GetWRefCon(window);
  457.             if (!(*frHndl)->doc.twoPlayer) {
  458.                 err = AEGetParamPtr(
  459.                     &message,            /* The AppleEvent.              */
  460.                     keyPascalReply,        /* AEKeyword                 */
  461.                     typePascal,            /* Desired type.             */
  462.                     &actualType,        /* Type code.                 */
  463.                     userName,            /* Pointer to area for data. */ 
  464.                     sizeof(Str255),        /* Size of data area.         */
  465.                     &actualSize            /* Returned size of data.     */
  466.                 );
  467.  
  468.                 (*frHndl)->doc.opponentName[0] = 0;
  469.                 if (!err) {
  470.                     (*frHndl)->doc.gameID_1 = gameID[0];
  471.                     pstrcpy(&(*frHndl)->doc.opponentName[0], userName);
  472.                     SetOpponentType(frHndl, kTwoPlayer);
  473.                 }
  474.                 else
  475.                     (*frHndl)->doc.gameID_0 = (*frHndl)->doc.gameID_1 = 0;
  476.             }
  477.         }
  478.     }
  479.  
  480.     return(err);
  481. }
  482.  
  483.  
  484.  
  485. /*****************************************************************************/
  486.  
  487.  
  488.  
  489. /* ReceiveGame
  490. **
  491. ** This routine receives a board from an opponent.  It receives it for various
  492. ** purposes.  These are:
  493. **   1) Establishing a new game.
  494. **   2) Receiving a move.
  495. **   3) Receiving a new board position, due to scrolling.
  496. **
  497. ** Establishing a new game is determined by the fact that the value twoPlayer
  498. ** is false.  If it is a previously established game, then this field is true.
  499. **
  500. ** If it is a previously established game, then whether or not it is a new move
  501. ** is determined by field "sendReason".  If sendReason != kIsMove, then it is some
  502. ** other change, such as scrolling or resyncing by of the sender.
  503. ** These other cases should not cause a win/loss/tie dialog to appear.
  504. **
  505. ** If it is a regular move, (sendReason == kIsMove), then the win/loss/draw dialogs
  506. ** should be displayed, if the game is indeed over after the move.
  507. */
  508.  
  509. #pragma segment AppleEvents
  510. pascal OSErr    ReceiveGame(AppleEvent message, AppleEvent reply, long refcon)
  511. {
  512. #pragma unused (refcon)
  513.  
  514.     OSErr            err;
  515.     FileRecHndl        newFrHndl, oldFrHndl;
  516.     AEDescList        receiveGameList;
  517.     char            hstate;
  518.     Ptr                ptr1, ptr2;
  519.     long            size, gameID[2];
  520.     Boolean            twoPlayer;
  521.     AEAddressDesc    senderTarget;
  522.     AEKeyword        ignoredKeyWord;
  523.     DescType        ignoredType;
  524.     Size            ignoredSize;
  525.     GameListHndl    gameMoves;
  526.     WindowPtr        oldPort, window;
  527.     FSSpec            myFSS;
  528.     Str255            userName;
  529.     Handle            userNameHndl;
  530.     short            i, drawBtnState, sendReason, myColor, invertBoard;
  531.     FileRecPtr        frPtr;
  532.     GameListHndl    oldGame, newGame;
  533.     GameElement        *optr, *nptr;
  534.     short            oldGameIndex, newGameIndex, oldNumMoves, newNumMoves, identical;
  535.  
  536.     err = noErr;
  537.     AEPutParamPtr(                /* RETURN REPLY ERROR, EVEN IF NONE... */
  538.         &reply,                    /* The AppleEvent.              */
  539.         keyReplyErr,            /* AEKeyword                 */
  540.         typeShortInteger,        /* Desired type.             */
  541.         (Ptr)&err,                /* Pointer to area for data. */ 
  542.         sizeof(short)            /* Size of data area.         */
  543.     );
  544.  
  545.     receiveGameList.dataHandle = nil;
  546.         /* Make sure disposing of the descriptors is okay in all cases.
  547.         ** This will not be necessary after 7.0b3, since the calls that
  548.         ** attempt to create the descriptors will nil automatically
  549.         ** upon failure. */
  550.  
  551.     IncNewFileNum(false);
  552.     err = AppNewDocument(&newFrHndl);
  553.     IncNewFileNum(true);
  554.  
  555.     if (err) return(err);
  556.  
  557.     /* We have a new document... */
  558.     err = AEGetParamDesc(    /* GET THE APPLEEVENT LIST.  */
  559.         &message,            /* AppleEvent to holding list.          */
  560.         keyDirectObject,    /* This is our direct (only) object.  */
  561.         typeWildCard,        /* Desired type is game list.          */
  562.         &receiveGameList    /* The list to add to the AppleEvent. */
  563.     );
  564.  
  565.     if (!err) {        /* If we got the list descriptor... */
  566.         hstate = LockHandleHigh((Handle)newFrHndl);
  567.         ptr1   = (Ptr)&((*newFrHndl)->doc);
  568.         ptr2   = (Ptr)&((*newFrHndl)->doc.endSendInfo);
  569.         size   = (long)ptr2 - (long)ptr1;
  570.         err = AEGetNthPtr(        /* GET BOARD INFO FROM THE LIST.     */
  571.             &receiveGameList,    /* List to get from.                 */
  572.             1L,                    /* Get first item in list.             */
  573.             typeTheBoard,        /* First item is the board.             */
  574.             &ignoredKeyWord,    /* Returned keyword -- we know.         */
  575.             &ignoredType,        /* Returned type -- we know.         */
  576.             ptr1,                /* Where to put the board info.         */
  577.             size,                /* Size of the board.                 */
  578.             &ignoredSize        /* Actual size -- we know.             */
  579.         );
  580.         HSetState((Handle)newFrHndl, hstate);
  581.     }
  582.  
  583.     if (!err) {        /* If we got the board... */
  584.         gameMoves = (*newFrHndl)->doc.gameMoves;
  585.         size      = (*newFrHndl)->doc.numGameMoves * sizeof(GameElement);
  586.         SetHandleSize((Handle)gameMoves, size);
  587.         err = MemError();
  588.         if (!err) {
  589.             hstate = LockHandleHigh((Handle)gameMoves);
  590.             err = AEGetNthPtr(        /* GET GAME MOVES FROM THE LIST.     */
  591.                 &receiveGameList,    /* List to get from.                 */
  592.                 2L,                    /* Get second item in list.             */
  593.                 typeGameMoves,        /* Second item is the game moves.     */
  594.                 &ignoredKeyWord,    /* Returned keyword -- we know.         */
  595.                 &ignoredType,        /* Returned type -- we know.         */
  596.                 (Ptr)(*gameMoves),    /* Where to put the board info.         */
  597.                 size,                /* Size of the board.                 */
  598.                 &ignoredSize        /* Actual size -- we know.             */
  599.             );
  600.             HSetState((Handle)gameMoves, hstate);
  601.         }
  602.     }
  603.  
  604.  
  605.  
  606.     if (!err) {
  607.  
  608.         /* We now have the board and game moves, in newFrHndl.  This is either a
  609.         ** new opponent, or an update from an old opponent.  Let's see... */
  610.  
  611.         twoPlayer = (*newFrHndl)->doc.twoPlayer;
  612.         (*newFrHndl)->doc.twoPlayer = false;
  613.             /* See if this is an already existing opponent, or a new one. */
  614.     
  615.         if (!twoPlayer) {        /* If new game... */
  616.             err = AEGetAttributeDesc(    /* GET ADDRESS OF NEW OPPONENT.     */
  617.                 &message,                /* Get address of sender from message.         */
  618.                 keyAddressAttr,            /* We want an address.                         */
  619.                 typeWildCard,            /* We want the address of the sender.         */
  620.                 &senderTarget            /* Address of sender.                         */
  621.             );
  622.             if (!err) {
  623.                 (*newFrHndl)->doc.twoPlayer     = true;
  624.                 (*newFrHndl)->doc.arrangeBoard  = false;
  625.                 (*newFrHndl)->doc.locOfOpponent = senderTarget;
  626.                 err = AEGetNthPtr(        /* GET FSSpec (FOR GAME NAME) FROM LIST. */
  627.                     &receiveGameList,    /* List to get from.            */
  628.                     3L,                    /* Get second item in list.        */
  629.                     typeFSS,            /* Third item is the FSSpec.    */
  630.                     &ignoredKeyWord,    /* Returned keyword -- we know. */
  631.                     &ignoredType,        /* Returned type -- we know.    */
  632.                     (Ptr)&myFSS,        /* Where to put the board info.    */
  633.                     sizeof(FSSpec),        /* Size of the data.            */
  634.                     &ignoredSize        /* Actual size -- we know.        */
  635.                 );
  636.                 if (!err)
  637.                     pstrcpy((*newFrHndl)->fileState.fss.name, myFSS.name);
  638.             }
  639.             if (!err) {
  640.                 err = AEGetNthPtr(        /* GET FSSpec (FOR GAME NAME) FROM LIST. */
  641.                     &receiveGameList,    /* List to get from.                */
  642.                     4L,                    /* Get second item in list.            */
  643.                     typePascal,            /* Third item is the opponent name.    */
  644.                     &ignoredKeyWord,    /* Returned keyword -- we know.     */
  645.                     &ignoredType,        /* Returned type -- we know.        */
  646.                     userName,            /* Where to put the board info.        */
  647.                     sizeof(Str255),        /* Size of the data.                */
  648.                     &ignoredSize        /* Actual size.                        */
  649.                 );
  650.                 (*newFrHndl)->doc.opponentName[0] = 0;
  651.                 if (!err)
  652.                     pstrcpy((*newFrHndl)->doc.opponentName, userName);
  653.             }
  654.         }
  655.     }
  656.  
  657.     if (!err) {        /* If we got the opponent address... */
  658.  
  659.         if (!twoPlayer) {        /* It is a new game... */
  660.  
  661.             (*newFrHndl)->doc.timerRefTick = TickCount();
  662.                 /* Set the timer reference early as possible.
  663.                 ** This syncs the two clocks as much as possible. */
  664.  
  665.                 /* For a new opponent, then we need to ID the receiver side
  666.                 ** of the sender/receiver ID pair.  We also need to send
  667.                 ** back the receiver portion of the ID pair for the sender. */
  668.  
  669.             (*newFrHndl)->doc.gameID_0 = gameID[0] = TickCount();
  670.             (*newFrHndl)->doc.myColor     ^= 1;
  671.             (*newFrHndl)->doc.invertBoard ^= 1;
  672.                 /* We are the opposite color/side as the opponent. */
  673.  
  674.             if ((*newFrHndl)->doc.version != kVersion) err = errAEWrongDataType;
  675.                 /* Incompatible file format. */
  676.  
  677.             if (!err) {
  678.                 gameID[1] = (*newFrHndl)->doc.gameID_1;
  679.                 err = AEPutParamPtr(    /* RETURN RECEIVER GAME ID.     */
  680.                     &reply,                /* The AppleEvent.              */
  681.                     keyGameID,            /* AEKeyword                 */
  682.                     typeDoubleLong,        /* Type code.                 */
  683.                     (Ptr)&gameID[0],    /* Pointer to area for data. */ 
  684.                     2 * sizeof(long)    /* Size of data area.         */
  685.                 );
  686.             }
  687.  
  688.             if (!err) {
  689.                 userName[0] = 0;
  690.                 if (userNameHndl = GetResource('STR ', -16096))
  691.                     pstrcpy(userName, (char *)(*userNameHndl));
  692.                 err = AEPutParamPtr(    /* RETURN RECEIVER GAME ID.     */
  693.                     &reply,                /* The AppleEvent.              */
  694.                     keyPascalReply,        /* AEKeyword                 */
  695.                     typePascal,            /* Type code.                 */
  696.                     userName,            /* Pointer to area for data. */ 
  697.                     userName[0] + 1        /* Size of data area.         */
  698.                 );
  699.             }
  700.  
  701.             if (!err) err = AppNewWindow(newFrHndl, nil);
  702.                 /* If everything worked for new opponent,
  703.                 ** give the new document a window. */
  704.  
  705.             if (!err) {
  706.                 AdjustGameSlider(newFrHndl);
  707.  
  708.                 DrawButtonTitle(newFrHndl, kTwoPlayer);
  709.                     /* Convert the draw button to two player. */
  710.  
  711.                 (*newFrHndl)->doc.gotUpdateTick = TickCount();
  712.                     /* Record when we got the event. */
  713.             }
  714.         }
  715.         else {        /* It is an update to an existing game. */
  716.  
  717.             window = GetGameWindow(
  718.                 (*newFrHndl)->doc.gameID_1, (*newFrHndl)->doc.gameID_0);
  719.                     /* Get the window for the existing game. */
  720.  
  721.             if (window) {        /* This game still exists... */
  722.  
  723.                 oldFrHndl = (FileRecHndl)GetWRefCon(window);
  724.                 UpdateTime(oldFrHndl, false);
  725.  
  726.                 if ((*oldFrHndl)->doc.resync < (sendReason = (*newFrHndl)->doc.sendReason))
  727.                     (*oldFrHndl)->doc.resync = sendReason;
  728.                         /* Bump up resync, if needed. */
  729.  
  730.                 if (sendReason == kScrolling) {
  731.                     if (
  732.                         ((*oldFrHndl)->doc.resync == kResync) || 
  733.                         ((*oldFrHndl)->doc.gotUpdateTick + 30 > TickCount())
  734.                     ) {
  735.                         window = nil;
  736.                         AppDisposeDocument(newFrHndl);
  737.                                 /* Temporary document now gone. */
  738.                     }    /* If it has been less than 1/2 second since the last
  739.                         ** scroll update received, or if the user has finished
  740.                         ** scrolling, then we can ignore this scroll event.
  741.                         ** This keeps us from getting behind in the processing
  742.                         ** of scroll events. */
  743.                 }
  744.             }
  745.             else
  746.                 err = memFullErr;
  747.                     /* Can't find the old window, so cause some kind of error. */
  748.  
  749.             if (window) {        /* This game still exists... */
  750.  
  751.                 if (sendReason != kIsMove) {
  752.                     for (i = 0; i < 2; ++i)
  753.                         (*newFrHndl)->doc.timeLeft[i] = (*oldFrHndl)->doc.timeLeft[i];
  754.                             /* Keep our clock values if opponent is scrolling or resyncing. */
  755.                 }
  756.  
  757.                 frPtr        = *oldFrHndl;
  758.                 oldGameIndex = frPtr->doc.gameIndex;
  759.                 oldNumMoves  = frPtr->doc.numGameMoves;
  760.                 oldGame      = frPtr->doc.gameMoves;
  761.                 frPtr        = *newFrHndl;
  762.                 newGameIndex = frPtr->doc.gameIndex;
  763.                 newNumMoves  = frPtr->doc.numGameMoves;
  764.                 newGame      = frPtr->doc.gameMoves;
  765.                 if ((oldGameIndex == newGameIndex) && (oldNumMoves == newNumMoves)) {
  766.                     optr = &(**oldGame)[0];
  767.                     nptr = &(**newGame)[0];
  768.                     identical = true;
  769.                     for (i = 0; i < oldNumMoves; ++i, ++optr, ++nptr) {
  770.                         if (
  771.                             (optr->moveFrom          != nptr->moveFrom) ||
  772.                             (optr->moveTo            != nptr->moveTo) ||
  773.                             (optr->pieceCaptured     != nptr->pieceCaptured) ||
  774.                             (optr->pieceCapturedFrom != nptr->pieceCapturedFrom) ||
  775.                             (optr->promoteTo         != nptr->promoteTo)
  776.                         ) {
  777.                             identical = false;
  778.                             break;
  779.                         }
  780.                     }
  781.                 }
  782.                 else identical = false;
  783.  
  784.                 myColor     = (*oldFrHndl)->doc.myColor;
  785.                 invertBoard = (*oldFrHndl)->doc.invertBoard;
  786.                     /* These need to be saved, so they are in that section,
  787.                     ** but they are private info, so cache them. */
  788.  
  789.                 ptr1 = (Ptr)&((*newFrHndl)->doc);
  790.                 ptr2 = (Ptr)&((*newFrHndl)->doc.endFileInfo);
  791.                 size = (long)ptr2 - (long)ptr1;
  792.                 ptr2 = (Ptr)&((*oldFrHndl)->doc);
  793.                 BlockMove(ptr1, ptr2, size);
  794.  
  795.                 (*oldFrHndl)->doc.myColor     = myColor;
  796.                 (*oldFrHndl)->doc.invertBoard = invertBoard;
  797.                     /* Restore our private cached values. */
  798.  
  799.                 (*newFrHndl)->doc.gameMoves = (*oldFrHndl)->doc.gameMoves;
  800.                 (*oldFrHndl)->doc.gameMoves = gameMoves;
  801.                     /* Swap the game move handles.  The existing document
  802.                     ** is now updated completely.  We can dispose of the
  803.                     ** temporary new one.  (gameMoves is set above for
  804.                     ** the handle for the new document.) */
  805.  
  806.                 i = (*newFrHndl)->doc.drawBtnState;
  807.                 drawBtnState = kTwoPlayer;
  808.                 if (i & 0x02) drawBtnState |= 0x04;
  809.                 if (i & 0x04) drawBtnState |= 0x02;
  810.  
  811.                 AppDisposeDocument(newFrHndl);
  812.                     /* Temporary document now gone. */
  813.  
  814.                 GetPort(&oldPort);
  815.                 SetPort(window);
  816.  
  817.                 DrawButtonTitle(oldFrHndl, drawBtnState);
  818.                 UpdateGameStatus(oldFrHndl);
  819.                     /* Update the text of the draw button. */
  820.  
  821.                 if (!identical) {        /* There was a change, so show it. */
  822.                     ImageDocument(oldFrHndl, true);
  823.                     AdjustGameSlider(oldFrHndl);
  824.                 }
  825.                 DrawTime(oldFrHndl);
  826.  
  827.                 (*oldFrHndl)->doc.gotUpdateTick = TickCount();
  828.                     /* Record when we got the event. */
  829.  
  830.                 if (sendReason == kIsMove) {
  831.                     (*oldFrHndl)->fileState.docDirty = true;
  832.                     if (GetCtlValue((*oldFrHndl)->doc.beepOnMove)) SysBeep(1);
  833.                     AlertIfGameOver(oldFrHndl);
  834.                         /* For non-moves, we don't want to beep or
  835.                         ** show a game-over dialog. */
  836.                 }
  837.                 SetPort(oldPort);
  838.             }
  839.         }
  840.     }
  841.  
  842.     if (err)
  843.         AppDisposeDocument(newFrHndl);
  844.             /* This won't get done twice, even though there is an
  845.             ** AppDisposeDocument(newFrHndl) earlier.  The earlier
  846.             ** one only happens if no error occured, and this one
  847.             ** only happens on an error condition. */
  848.  
  849.     AEDisposeDesc(&receiveGameList);
  850.         /* Dispose of the descriptors, created or not.
  851.         ** If not created, no harm done by calling. */
  852.  
  853.     if (!err) NotifyUser();
  854.     return(err);
  855. }
  856.  
  857.  
  858.  
  859. /*****************************************************************************/
  860.  
  861.  
  862.  
  863. /* SendMssg
  864. **
  865. ** Send one of various messages to the opponent.  There is a common set of
  866. ** data that needs to be sent so that the opponent can determine which game
  867. ** the message should be applied.  Then there is message-specific data that
  868. ** is handled case by case.  Once the message is completed, it is sent off
  869. ** to the opponent.  The additional task of updating the state of our game
  870. ** is also handled here.  For example:  When text is sent, the text on our
  871. ** machine is selected to make it easier to replace the old text with new
  872. ** text.  Any typing by the user will replace the selected text (all the text)
  873. ** with the newly typed text.  */
  874.  
  875. #pragma segment AppleEvents
  876. Boolean    SendMssg(FileRecHndl frHndl, short messageType)
  877. {
  878.     AEAddressDesc    locOfOpponent;
  879.     OSErr            err;
  880.     TEHandle        te;
  881.     char            hstate;
  882.     AppleEvent        theAevt, reply;
  883.     Handle            hText, snd;
  884.     long            size, gameID[2], time[2];
  885.     short            i;
  886.     Boolean            twoPlayer;
  887.     WindowPtr        oldPort;
  888.  
  889.     oldPort = SetFilePort(frHndl);
  890.  
  891.     theAevt.dataHandle = reply.dataHandle = nil;
  892.         /* Make sure disposing of the descriptors is okay in all cases.
  893.         ** This will not be necessary after 7.0b3, since the calls that
  894.         ** attempt to create the descriptors will nil automatically
  895.         ** upon failure. */
  896.  
  897.     err = noErr;
  898.  
  899.     if (twoPlayer = (*frHndl)->doc.twoPlayer) {
  900.  
  901.         locOfOpponent = (*frHndl)->doc.locOfOpponent;
  902.  
  903.         err = AECreateAppleEvent(        /* CREATE EMPTY APPLEEVENT.     */
  904.             kCustomEventClass,            /* Event class.                 */
  905.             kibitzAESendMssg,            /* Event ID.                 */
  906.             &locOfOpponent,                /* Address of receiving app. */
  907.             kAutoGenerateReturnID,        /* This value causes the     */
  908.                                         /* AppleEvent manager to     */
  909.                                         /* assign a return ID that     */
  910.                                         /* is unique to the session. */
  911.             kAnyTransactionID,            /* Ignore transaction ID.     */
  912.             &theAevt                    /* Location of event.         */
  913.         );
  914.  
  915.         if (!err) {            /* Say what the message is. */
  916.             AEPutParamPtr(
  917.                 &theAevt,
  918.                 keyDirectObject,
  919.                 typeShortInteger,
  920.                 (Ptr)&messageType,
  921.                 sizeof(short)
  922.             );
  923.         }
  924.  
  925.         if (!err) {            /* Say what window message is for. */
  926.             gameID[0] = (*frHndl)->doc.gameID_0;
  927.             gameID[1] = (*frHndl)->doc.gameID_1;
  928.             AEPutParamPtr(
  929.                 &theAevt,
  930.                 keyGameID,
  931.                 typeDoubleLong,
  932.                 (Ptr)&gameID[0],
  933.                 2 * sizeof(long)
  934.             );
  935.         }
  936.     }
  937.  
  938.     /* The stuff that applies to all messages is now done.  Now specifically
  939.     ** handle all the different message types. */
  940.  
  941.     if (!err) {
  942.         switch (messageType) {
  943.  
  944.             case kAmWhiteMssg:
  945.             case kAmBlackMssg:
  946.                 (*frHndl)->doc.configColor       = messageType;
  947.                 (*frHndl)->doc.configColorChange = true;
  948.                 (*frHndl)->doc.resync            = kHandResync;
  949.                     /* The AppleEvent already has all the data the opponent
  950.                     ** needs.  Post that a color change is happening.  The
  951.                     ** reason that it is posted, instead of immediately applied,
  952.                     ** is that we want to make sure that the opponent isn't
  953.                     ** doing a color change operation at the same time.  The
  954.                     ** color change will only occur when we get a NULL event.
  955.                     ** The NULL event indicates that the opponent isn't sending
  956.                     ** any AppleEvents at that time.  Waiting for quiescence
  957.                     ** helps prevent the state of the game from getting
  958.                     ** confused.  Also, the creator of the game echos the
  959.                     ** color change, just to make sure that both machines are
  960.                     ** in the same state.  (The echo doesn't occur until
  961.                     ** there are no other AppleEvents happening.  This helps
  962.                     ** keep the number of AppleEvents down, as well.) */
  963.                 break;
  964.  
  965.             case kDisconnectMssg:
  966.                     /* All the information we need is already in the AppleEvent. */
  967.                 break;
  968.  
  969.             case kTimeMssg:
  970.                 for (i = 0; i < 2; ++i)
  971.                     time[i] = (*frHndl)->doc.configTime[i] = (*frHndl)->doc.timeLeft[i];
  972.                 if (twoPlayer) {
  973.                     err = AEPutParamPtr(
  974.                         &theAevt,
  975.                         keyTime,
  976.                         typeDoubleLong,
  977.                         (Ptr)&time[0],
  978.                         2 * sizeof(long)
  979.                     );
  980.                     if ((*frHndl)->doc.resync)
  981.                         (*frHndl)->doc.configTimeChange = true;
  982.                     else {
  983.                         for (i = 0; i < 2; ++i)
  984.                             (*frHndl)->doc.timeLeft[i] =
  985.                                 (*frHndl)->doc.displayTime[i] =
  986.                                     (*frHndl)->doc.configTime[i];
  987.                         UpdateTime(frHndl, false);
  988.                         DrawTime(frHndl);
  989.                     }
  990.                 }
  991.                 break;
  992.  
  993.             case kTextMssg:
  994.                 if (twoPlayer) {
  995.                     te     = (*frHndl)->doc.message[1];
  996.                     hText  = (*te)->hText;
  997.                     hstate = LockHandleHigh(hText);
  998.                     size   = (*te)->teLength;
  999.                     err = AEPutParamPtr(
  1000.                         &theAevt,
  1001.                         keyTextMessage,
  1002.                         typeMssg,
  1003.                         *hText,
  1004.                         size
  1005.                     );
  1006.                     HSetState(hText, hstate);
  1007.                 }
  1008.                 break;
  1009.  
  1010.             case kSoundMssg:
  1011.                 if (twoPlayer) {
  1012.                     if (snd = (*frHndl)->doc.sound) {
  1013.                         hstate = LockHandleHigh(snd);
  1014.                         size   = GetHandleSize(snd);
  1015.                         err = AEPutParamPtr(
  1016.                             &theAevt,
  1017.                             keySoundMessage,
  1018.                             typeMssg,
  1019.                             *snd,
  1020.                             size
  1021.                         );
  1022.                         HSetState(snd, hstate);
  1023.                     }
  1024.                 }
  1025.                 break;
  1026.         }
  1027.     }
  1028.  
  1029.     if (twoPlayer) {
  1030.         if (!err) {        /* If everything looks good... */
  1031.             err = AESend(                /* SEND APPLEEVENT.                */
  1032.                 &theAevt,                /* Our Apple Event to send.        */
  1033.                 &reply,                    /* We may have a reply.            */
  1034.                 kAENoReply,                /* Don't wait for reply.        */
  1035.                 PRIORITY,                /* App. send priority.            */
  1036.                 0,                        /* We aren't waiting.            */
  1037.                 (IdleProcPtr)MyIdleProc, /* Our only shot at doing some    */
  1038.                                          /* work if we are waiting.        */
  1039.                 nil                        /* EventFilterProcPtr.            */
  1040.             );
  1041.         }
  1042.         if (!err) {
  1043.             switch (messageType) {
  1044.                 case kTextMssg:
  1045.                     CTESetSelect(0, (*te)->teLength, te);
  1046.                         /* Select all the text so entering the next message
  1047.                         ** is more convenient. */
  1048.                     break;
  1049.                 case kSoundMssg:
  1050.                     SndPlay(nil, (*frHndl)->doc.sound, false);
  1051.                     break;
  1052.             }
  1053.         }
  1054.  
  1055.         AEDisposeDesc(&theAevt);
  1056.         AEDisposeDesc(&reply);
  1057.             /* Dispose of the descriptors, created or not.
  1058.             ** If not created, no harm done by calling. */
  1059.     }
  1060.  
  1061.     SetPort(oldPort);
  1062.     return(err);
  1063. }
  1064.  
  1065.  
  1066.  
  1067. /*****************************************************************************/
  1068.  
  1069.  
  1070.  
  1071. #pragma segment AppleEvents
  1072. pascal OSErr    ReceiveMssg(AppleEvent message, AppleEvent reply, long refcon)
  1073. {
  1074. #pragma unused (refcon)
  1075.  
  1076.     OSErr            err;
  1077.     long            gameID[2], time[2];
  1078.     short            messageType, i;
  1079.     WindowPtr        window;
  1080.     FileRecHndl        frHndl;
  1081.     DescType        actualType;
  1082.     long            actualSize, mssgSize;
  1083.     unsigned long    key;
  1084.     char            hstate;
  1085.     Handle            mssgData;
  1086.     AEAddressDesc    locOfOpponent;
  1087.  
  1088.     err = noErr;
  1089.     AEPutParamPtr(                /* RETURN REPLY ERROR, EVEN IF NONE... */
  1090.         &reply,                    /* The AppleEvent.              */
  1091.         keyReplyErr,            /* AEKeyword                 */
  1092.         typeShortInteger,        /* Desired type.             */
  1093.         (Ptr)&err,                /* Pointer to area for data. */ 
  1094.         sizeof(short)            /* Size of data area.         */
  1095.     );
  1096.  
  1097.     err = AEGetParamPtr(        /* GET THE MESSAGE TYPE.     */
  1098.         &message,                /* The AppleEvent.              */
  1099.         keyDirectObject,        /* AEKeyword                 */
  1100.         typeShortInteger,        /* Desired type.             */
  1101.         &actualType,            /* Type code.                 */
  1102.         (Ptr)&messageType,        /* Pointer to area for data. */ 
  1103.         2 * sizeof(long),        /* Size of data area.         */
  1104.         &actualSize                /* Returned size of data.     */
  1105.     );
  1106.  
  1107.     if (!err) {
  1108.         err = AEGetParamPtr(        /* GET WINDOW MESSAGE IS FOR. */
  1109.             &message,                /* The AppleEvent.               */
  1110.             keyGameID,                /* AEKeyword                  */
  1111.             typeDoubleLong,            /* Desired type.              */
  1112.             &actualType,            /* Type code.                  */
  1113.             (Ptr)&gameID[0],        /* Pointer to area for data.  */ 
  1114.             2 * sizeof(long),        /* Size of data area.          */
  1115.             &actualSize                /* Returned size of data.      */
  1116.         );
  1117.     }
  1118.  
  1119.     if (!err) {            /* See if the requested window exists... */
  1120.         if (window = GetGameWindow(gameID[1], gameID[0])) {
  1121.             frHndl = (FileRecHndl)GetWRefCon(window);
  1122.                 /* The game still exists... */
  1123.         }
  1124.         else
  1125.             err = userCanceledErr;
  1126.                 /* User (or computer) canceled game by disconnecting improperly. */
  1127.     }
  1128.  
  1129.     if (!err) {        /* If everything is cool, then do the specific task... */
  1130.  
  1131.         switch(messageType) {
  1132.  
  1133.             case kAmWhiteMssg:
  1134.             case kAmBlackMssg:
  1135.                 messageType ^= 1;
  1136.                 (*frHndl)->doc.configColor       = messageType;
  1137.                 (*frHndl)->doc.configColorChange = true;
  1138.                 (*frHndl)->doc.resync            = kHandResync;
  1139.                 if ((*frHndl)->doc.creator)
  1140.                     SendMssg(frHndl, messageType);
  1141.                         /* If we are the creator, echo the message to make sure
  1142.                         ** that both players aren't the same color. */
  1143.                 break;
  1144.  
  1145.             case kDisconnectMssg:
  1146.                 locOfOpponent = (*frHndl)->doc.locOfOpponent;
  1147.                 AEDisposeDesc(&locOfOpponent);
  1148.                 (*frHndl)->doc.twoPlayer = kLimbo;
  1149.                     /* We set the state of the game to NOT two-player, and NOT
  1150.                     ** what we are now.  This allows us to call SetOpponentType
  1151.                     ** to do all the work we need done.  If we didn't fudge the
  1152.                     ** state of the game (from two-player) then SetOpponentType
  1153.                     ** would send a disconnect message (and we would end up
  1154.                     ** back here.)  This would be an ugly situation, which is
  1155.                     ** completely prevented by fudging the state of the game. */
  1156.                 SetOpponentType(frHndl, kOnePlayer);
  1157.                 break;
  1158.  
  1159.             case kTimeMssg:
  1160.                 AEGetParamPtr(
  1161.                     &message,                /* The AppleEvent.              */
  1162.                     keyTime,                /* AEKeyword                 */
  1163.                     typeDoubleLong,            /* Desired type.             */
  1164.                     &actualType,            /* Type code.                 */
  1165.                     (Ptr)&time[0],            /* Pointer to area for data. */ 
  1166.                     2 * sizeof(long),        /* Size of data area.         */
  1167.                     &mssgSize                /* Returned size of data.     */
  1168.                 );
  1169.                 for (i = 0; i < 2; ++i)
  1170.                     (*frHndl)->doc.configTime[i] = time[i];
  1171.                 if ((*frHndl)->doc.resync)
  1172.                     (*frHndl)->doc.configTimeChange = true;
  1173.                 else {
  1174.                     for (i = 0; i < 2; ++i)
  1175.                         (*frHndl)->doc.timeLeft[i] =
  1176.                             (*frHndl)->doc.displayTime[i] = time[i];
  1177.                     UpdateTime(frHndl, false);
  1178.                     DrawTime(frHndl);
  1179.                 }
  1180.                 if ((*frHndl)->doc.creator)
  1181.                     SendMssg(frHndl, kTimeMssg);
  1182.                         /* If we are the creator, echo the message to make sure
  1183.                         ** the clocks are in sync. */
  1184.                 break;
  1185.  
  1186.             case kTextMssg:
  1187.             case kSoundMssg:
  1188.                 /* Both the text and sound message simply send a block of
  1189.                 ** data less than 32k.  Get the data from the AppleEvent
  1190.                 ** for both cases, and then decide what kind of data it is. */
  1191.  
  1192.                 key = (messageType == kTextMssg) ? keyTextMessage : keySoundMessage;
  1193.                 if (!err) {        /* Determine the size of the data... */
  1194.                     err = AEGetParamPtr(
  1195.                         &message,                /* The AppleEvent.              */
  1196.                         key,                    /* AEKeyword                 */
  1197.                         typeMssg,                /* Desired type.             */
  1198.                         &actualType,            /* Type code.                 */
  1199.                         nil,                    /* Pointer to area for data. */ 
  1200.                         0,                        /* Size of data area.         */
  1201.                         &mssgSize                /* Returned size of data.     */
  1202.                     );
  1203.                 }
  1204.                 mssgData = nil;
  1205.                 if (!err) {        /* Get the data... */
  1206.                     mssgData = NewHandle(mssgSize);
  1207.                     if (mssgData) {
  1208.                         hstate = LockHandleHigh(mssgData);
  1209.                         err = AEGetParamPtr(
  1210.                             &message,                /* The AppleEvent.              */
  1211.                             key,                    /* AEKeyword                 */
  1212.                             typeMssg,                /* Desired type.             */
  1213.                             &actualType,            /* Type code.                 */
  1214.                             *mssgData,                /* Pointer to area for data. */ 
  1215.                             mssgSize,                /* Size of data area.         */
  1216.                             &actualSize                /* Returned size of data.     */
  1217.                         );
  1218.                     }
  1219.                     else err = memFullErr;
  1220.                 }
  1221.                 if (!err) {
  1222.                     if (messageType == kTextMssg) {
  1223.                         mssgData = CTESwapText((*frHndl)->doc.message[0], mssgData, true);
  1224.                         if (GetCtlValue((*frHndl)->doc.beepOnMssg))
  1225.                             if (mssgSize) SysBeep(1);
  1226.                     }
  1227.                     else SndPlay(nil, mssgData, false);
  1228.                 }
  1229.                 if (mssgData) DisposHandle(mssgData);
  1230.                 if (!err) NotifyUser();
  1231.                 break;
  1232.  
  1233.         }
  1234.     }
  1235.  
  1236.     return(err);
  1237. }
  1238.  
  1239.  
  1240.  
  1241. /*****************************************************************************/
  1242. /*****************************************************************************/
  1243.  
  1244.  
  1245.  
  1246. /* GetGameWindow
  1247. **
  1248. ** Find the window with the specified game ID's.
  1249. */
  1250.  
  1251. #pragma segment AppleEvents
  1252. WindowPtr    GetGameWindow(long gameID_0, long gameID_1)
  1253. {
  1254.     WindowPeek    window;
  1255.     FileRecHndl    frHndl;
  1256.  
  1257.     for (window = *(WindowPeek *)WindowList; window; window = window->nextWindow) {
  1258.         if (IsAppWindow((WindowPtr)window)) {
  1259.             frHndl = (FileRecHndl)GetWRefCon((WindowPtr)window);
  1260.             if (
  1261.                 ((*frHndl)->doc.gameID_0 == gameID_0) &&
  1262.                 ((*frHndl)->doc.gameID_1 == gameID_1)
  1263.             ) return((WindowPtr)window);
  1264.         }
  1265.     }
  1266.  
  1267.     return(nil);
  1268. }
  1269.  
  1270.  
  1271.  
  1272. /*****************************************************************************/
  1273.  
  1274.  
  1275.  
  1276. /* KibitzPortFilter
  1277. **
  1278. ** Don't allow PPCBrowser to show any applications other than Kibitz.
  1279. */
  1280.  
  1281. #pragma segment AppleEvents
  1282. pascal Boolean    KibitzPortFilter(LocationNamePtr locationName, PortInfoPtr thePortInfo)
  1283. {
  1284. #pragma unused (locationName)
  1285.  
  1286.     long    type;
  1287.  
  1288.     if (thePortInfo->name.portKindSelector == ppcByString) {
  1289.         BlockMove(thePortInfo->name.u.portTypeStr + 1, (Ptr)&type, 4);
  1290.             /* The BlockMove is so that we don't get an address error
  1291.             ** on a 68000-based machine due to referencing a long at
  1292.             ** an odd-address. */
  1293.         if (type == docCreator) return(true);
  1294.     }
  1295.  
  1296.     return(false);
  1297. }
  1298.  
  1299.  
  1300.  
  1301. /*****************************************************************************/
  1302.  
  1303.  
  1304.  
  1305. /* MyIdleProc
  1306. **
  1307. ** This routine gets either an updateEvt, an activateEvt, a nullEvent or an
  1308. ** OSEvent.  The first time called it will get a nullEvent; this is its chance
  1309. ** to set the sleep value and mouseRegion that will be passed to WaitNextEvent
  1310. ** by the caller within the AEM.  After that it will also get events of the
  1311. ** other types (which are lost if masked out by the AEM) and must do with them
  1312. ** whatever is appropriate.
  1313. */
  1314.  
  1315. #pragma segment AppleEvents
  1316. pascal Boolean    MyIdleProc(EventRecord *event, long *sleep, RgnHandle *mouseRgn)
  1317. {
  1318.     switch (event->what) {
  1319.  
  1320.         case updateEvt:
  1321.         case activateEvt:
  1322.         case kOSEvent:
  1323.  
  1324.             /* These events are passed by the AppleEvent manager to avoid
  1325.                dropping while waiting for a reply or notification.  Every
  1326.                procedure should handle these events in their idle procedure.
  1327.                In this code, we simply dispatch these events back to the
  1328.                main event loop handling code. */
  1329.  
  1330.             DoCursor(false, 0);
  1331.             DoEvent(event);
  1332.             break;
  1333.  
  1334.  
  1335.         case nullEvent:
  1336.  
  1337.             /* The idle procedure is called once with the null event before
  1338.                the loop begins.  This allows the application to alter sleep
  1339.                time and mouseRgn to meet its own needs.  Since we're doing
  1340.                nothing, set the cursor to a watch. */
  1341.  
  1342.             DoCursor(true, 'wait');
  1343.  
  1344.             *mouseRgn = gCurrentCursorRgn;
  1345.             *sleep = 60;        /* This is just like the WaitNextEvent
  1346.                                    sleeptime, so use the correct value for
  1347.                                    your application.  It's better to use a
  1348.                                    non-zero value here rather than zero,
  1349.                                    as using zero really slows you down. */
  1350.  
  1351.             /* DoIdle(); */        /* Application's idle handling. */
  1352.             break;
  1353.  
  1354.         default:
  1355.             Alert(rErrorAlert, nil);
  1356.             break;
  1357.     }
  1358.     return(false);
  1359. }
  1360.  
  1361.  
  1362. /*****************************************************************************/
  1363.  
  1364.  
  1365.  
  1366. #pragma segment AppleEvents
  1367. void    NotifyCancel(void)
  1368. {
  1369.     if (notifyActive) {
  1370.         NMRemove((NMRecPtr)¬ifyTheUser);
  1371.         notifyActive = false;
  1372.     }
  1373. }
  1374.  
  1375.  
  1376.  
  1377.  
  1378. /*****************************************************************************/
  1379.  
  1380.  
  1381.  
  1382. #pragma segment AppleEvents
  1383. void    NotifyUser(void)
  1384. {
  1385.     if (gInBackground) {
  1386.         if (!notifyActive) {
  1387.             notifyTheUser.nmIcon = GetResource('SICN', 128);
  1388.             NMInstall(¬ifyTheUser);
  1389.             notifyActive = true;
  1390.         }
  1391.     }
  1392. }
  1393.  
  1394.  
  1395.  
  1396. /*****************************************************************************/
  1397.  
  1398.  
  1399.  
  1400. /* SetOpponentType
  1401. **
  1402. ** Change the opponent type from whatever it currently is to the specified
  1403. ** type.  In so doing, change the state of any controls, etc., that need
  1404. ** to be changed due to the new opponent type.
  1405. */
  1406.  
  1407. #pragma segment AppleEvents
  1408. void    SetOpponentType(FileRecHndl frHndl, short newOpponentType)
  1409. {
  1410.     WindowPtr        oldPort, window;
  1411.     short            oldOpponentType, hilite, i, toggle;
  1412.     AEAddressDesc    locOfOpponent;
  1413.     Rect            boardRect;
  1414.     ControlHandle    ctl;
  1415.     RgnHandle        oldClip, newClip;
  1416.  
  1417.     if (!(oldOpponentType = (*frHndl)->doc.arrangeBoard))
  1418.         oldOpponentType = (*frHndl)->doc.twoPlayer;
  1419.     if (oldOpponentType == newOpponentType) return;
  1420.  
  1421.     (*frHndl)->doc.arrangeBoard = false;
  1422.  
  1423.     oldPort = SetFilePort(frHndl);
  1424.     GetPort(&window);
  1425.  
  1426.     oldClip = NewRgn();
  1427.     newClip = NewRgn();
  1428.     GetClip(oldClip);
  1429.  
  1430.     toggle = false;
  1431.     if ((newOpponentType == kArrangeBoard) || (oldOpponentType == kArrangeBoard)) {
  1432.         toggle = true;
  1433.         SetClip(newClip);        /* Prevent stuff from drawing. */
  1434.     }
  1435.  
  1436.     if (oldOpponentType == kTwoPlayer) {
  1437.         SendMssg(frHndl, kDisconnectMssg);
  1438.         locOfOpponent = (*frHndl)->doc.locOfOpponent;
  1439.         AEDisposeDesc(&locOfOpponent);
  1440.     }
  1441.  
  1442.     if (newOpponentType == kTwoPlayer) {
  1443.         hilite = 0;
  1444.         (*frHndl)->doc.timerRefTick = TickCount();
  1445.         (*frHndl)->doc.creator = true;
  1446.     }
  1447.     else {
  1448.         hilite = 255;
  1449.         (*frHndl)->doc.creator = false;
  1450.         (*frHndl)->doc.resync  = kIsMove;
  1451.         (*frHndl)->doc.gameID_0 = (*frHndl)->doc.gameID_1 = 0;
  1452.     }
  1453.  
  1454.     SetPort(window);
  1455.  
  1456.     if (newOpponentType == kArrangeBoard) {
  1457.         CTEDeactivate();
  1458.         (*frHndl)->doc.king[BLACK].rookMoves[QSIDE] = 0;
  1459.         (*frHndl)->doc.king[BLACK].rookMoves[KSIDE] = 0;
  1460.         (*frHndl)->doc.king[WHITE].rookMoves[QSIDE] = 0;
  1461.         (*frHndl)->doc.king[WHITE].rookMoves[KSIDE] = 0;
  1462.         (*frHndl)->doc.enPasMove        = 0;
  1463.         (*frHndl)->doc.enPasPawnLoc     = 0;
  1464.         (*frHndl)->doc.arngEnPasMove    = 0;
  1465.         (*frHndl)->doc.arngEnPasPawnLoc = 0;
  1466.         (*frHndl)->doc.numLegalMoves    = 0;
  1467.         (*frHndl)->doc.gameIndex        = 0;
  1468.         (*frHndl)->doc.numGameMoves     = 0;
  1469.         (*frHndl)->doc.compMovesWhite   = false;
  1470.         (*frHndl)->doc.compMovesBlack   = false;
  1471.     }
  1472.     if (oldOpponentType == kArrangeBoard)
  1473.         CTEWindActivate(window, true);
  1474.  
  1475.     if (newOpponentType == kArrangeBoard) {
  1476.         (*frHndl)->doc.arrangeBoard = kArrangeBoard;
  1477.         newOpponentType = kOnePlayer;
  1478.     }
  1479.     (*frHndl)->doc.twoPlayer = newOpponentType;
  1480.  
  1481.     HiliteControl(ctl = (*frHndl)->doc.sendMessage, hilite);
  1482.     OutlineControl(ctl);
  1483.     HiliteControl((*frHndl)->doc.beepOnMove, hilite);
  1484.     HiliteControl((*frHndl)->doc.beepOnMssg, hilite);
  1485.  
  1486.     if (!SoundInputAvaliable()) hilite = 255;
  1487.     HiliteControl((*frHndl)->doc.record, hilite);
  1488.     if (!(*frHndl)->doc.sound) hilite = 255;
  1489.     HiliteControl((*frHndl)->doc.sendSnd, hilite);
  1490.     DrawButtonTitle(frHndl, newOpponentType);
  1491.  
  1492.     if (toggle) {
  1493.         SetClip(oldClip);
  1494.         for (i = 0; i < 2; ++i) (*frHndl)->doc.timeLeft[i] = -1;
  1495.         boardRect = BoardRect();
  1496.         boardRect.left = boardRect.right + 4;
  1497.         boardRect.right = 1000;
  1498.         EraseRect(&boardRect);
  1499.         ImageDocument(frHndl, false);
  1500.     }
  1501.  
  1502.     DisposeRgn(oldClip);
  1503.     DisposeRgn(newClip);
  1504.     SetPort(oldPort);
  1505. }
  1506.  
  1507.  
  1508.  
  1509.